import csv
import json

import cv2
import numpy as np


# 判断在哪一列，根据竖线列表和一个点的坐标判断，返回所在列的索引
def where_insert(x: int, col_list: list):
    for i in range(len(col_list) - 1):
        # 在第j条竖线和第j+1条竖线之间
        if col_list[i] < x < col_list[i + 1]:
            return i
    return None


# 封装一个单元格
def group_cell(cell: list):
    # 将单元格中的字符按坐标排序
    cell.sort(key=lambda x: (x[1] + x[3]))
    # 获取最大和最小的横坐标
    min_x1 = min(cell[0][1], cell[0][3])
    max_x2 = max(cell[-1][1], cell[-1][3])

    s = ""
    min_y1 = cell[0][2]
    max_y2 = cell[-1][4]

    # 遍历每一个单元格中的字符
    for box in cell:
        s += box[0]
        # 更新y1
        if min_y1 > box[2]:
            min_y1 = box[2]
        #  更新y2
        if max_y2 < box[4]:
            max_y2 = box[4]
    # 将单元格重新封装
    new_line_cell = [s, [min_x1, min_y1], [max_x2, min_y1], [max_x2, max_y2], [min_x1, max_y2]]
    return new_line_cell


# 多次识别
def export_line_data(height: int, boxes: list, col_list: list, min_column: int):
    col_num = len(col_list) - 1
    # 初始化一个新的空数组，用于存储新的线条
    r_lines = []
    # 遍历每一行,反向遍历
    for y in range(height, 0, -1):
        # 初始化相交的方框数组
        intersection_boxes = [None for _ in range(col_num)]
        # 计数器
        counter = 0

        # 遍历所有的方框
        for box in boxes:
            # 获取方框坐标
            coords = box[1:5]
            # 方框的中点横坐标
            x = (coords[0][0] + coords[1][0] + coords[2][0] + coords[3][0]) // 4
            # 获取方框在y方向上的范围
            max_y = max(coords[0][1], coords[1][1], coords[2][1], coords[3][1])
            min_y = min(coords[0][1], coords[1][1], coords[2][1], coords[3][1])
            # 判断是否与方框相交
            if min_y <= y <= max_y:
                # 如果相交的话，填到对应的列当中
                col_index = where_insert(x, col_list)
                # 存在
                if col_index is not None:
                    intersection_boxes[col_index] = box
                    counter += 1  # 自增

        # 如果满足条件的话1
        if counter >= min_column:
            # 删除原本数据当中的方框
            for intersection_box in intersection_boxes:
                if intersection_box is not None:
                    boxes.remove(intersection_box)
            # 新结构，包含 y值 和 这条线上的所有方框
            r_lines.append([y, intersection_boxes])
    return r_lines


# 合并两个列表，如果可以合并返回新列表，如果不能合并就返回 None
def merge_two_lines(line1: list, line2: list):
    cell_num = len(line1)
    if len(line1) != len(line2):
        return None

    # 初始化新列表
    new_line = []
    for i in range(cell_num):
        # 有冲突
        if (line1[i] is not None) and (line2[i] is not None):
            return None
        else:
            # 没有冲突就合并
            new_line.append(line1[i] if line1[i] is not None else line2[i])

    return new_line


# 将可能合并的行做合并操作
def merge_may_lines(lines: list):
    lines_cell = [_[1] for _ in lines]
    lines_y = [_[0] for _ in lines]

    # 计算平均行间距
    line_num = len(lines_cell)
    reduce_line_spacing = 0  # 用来累加线条的差值
    for i in range(line_num - 1):
        reduce_line_spacing += abs(lines_y[i] - lines_y[i + 1])
    # 平均差值
    avg_reduce_line_spacing = reduce_line_spacing // line_num

    # 新的行列表,并初始化第一行
    new_lines = [lines_cell[0]]

    # 指针p
    p = 1
    while p < len(lines_cell):
        # 合并新列表中的最新行，和当前指针所在的行
        merged_line = merge_two_lines(new_lines[-1], lines_cell[p])
        # 不是 None 说明合并成功
        if merged_line is not None:
            new_lines[-1] = merged_line
        # 不能合并就是一个新的行
        else:
            new_lines.append(lines_cell[p])
        p += 1
    return new_lines


# 通过画线判断的方法获得所有行的单元格
def export_all_line_data(image, boxes: list, col_list: list):
    # 第一次应该设置为列数（竖线数-1）
    min_column = len(col_list) - 1
    # 获取输入图像的高度、宽度
    height, width = image.shape[:2:]
    # 复制方框数组
    boxes_copy = boxes.copy()
    # 存放所有检测出来的行
    lines = []

    # 多次画线
    while min_column > 0:
        line = export_line_data(height=height, boxes=boxes_copy, col_list=col_list, min_column=min_column)
        lines.extend(line)
        min_column //= 2  # 折半

    # 按照y值排序
    lines.sort(key=lambda x: x[0], reverse=True)

    # 相邻的可能可以合并(处理过后的列表已经将前面的 y 去除了)
    new_lines_cell = merge_may_lines(lines)

    # 画图观察
    img = cv2.cvtColor(image, cv2.COLOR_GRAY2BGR)
    for line_cell in new_lines_cell:
        for box in line_cell:
            x1, y1 = box[1]
            x2, y2 = box[3]
            cv2.rectangle(img, (x1, height - y1), (x2, height - y2), (255, 0, 255), 5)
            cv_show("img", img)
    return new_lines_cell


# 将坐标排序
def approx_sort(approx):
    r = np.copy(approx)
    x, y, w, h = cv2.boundingRect(r)

    # 要旋转：只需交换顺序即可
    # 中点位置
    center_x, center_y = x + w // 2, y + h // 2
    new_approx = []

    # ↙
    for p in r:
        if center_x > p[0][0] and center_y > p[0][1]:
            new_approx.append(p)
            break

    # ↘
    for p in r:
        if center_x < p[0][0] and center_y > p[0][1]:
            new_approx.append(p)
            break

    # ↗
    for p in r:
        if center_x < p[0][0] and center_y < p[0][1]:
            new_approx.append(p)
            break

    # ↖
    for p in r:
        if center_x > p[0][0] and center_y < p[0][1]:
            new_approx.append(p)
            break

    return np.array(new_approx)


# 数据转为json格式保存
def list_to_json(data):
    new_data = []
    # 将规范好的数据格式变为json
    for line in data:
        new_cell = []
        for cell in line:
            new_cell.append(str(cell[0]))
        if len(new_cell) != 0:
            new_data.append(new_cell)
    return new_data


# 把json格式装换为csv存储
def json_to_csv(data):
    pass


# 保留scv和json文件
def save_csv_file(data, ocr_path, ojr_path=None):
    # 保留 csv
    with open(ocr_path, mode='w', newline='') as json_file:
        writer = csv.writer(json_file)
        writer.writerows(data)

    # 如果没有地址，不保存 json 文件
    if ojr_path is not None:
        # 将结果转换为 JSON 字符串
        data_json_str = json.dumps(data)

        # 将 JSON 字符串保存到文件
        with open(ojr_path, 'w') as json_file:
            json_file.write(data_json_str)
